package com.rizzo.infrastructure.performance.redis;

import com.rizzo.infrastructure.amqp.rabbit.log4j.LogStatement;
import com.rizzo.infrastructure.aop.MethodProceeding;
import com.rizzo.infrastructure.performance.PerformanceMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.StopWatch;

/**
 *
 */
public class LoggingPerformanceMonitor implements PerformanceMonitor {

	protected transient Logger defaultLogger = LoggerFactory.getLogger(getClass());

	private String prefix = "";

	private String suffix = "";

	private SecurityContextHolderStrategy securityContextHolderStrategy;

	public LoggingPerformanceMonitor() {
	}

	public LoggingPerformanceMonitor(boolean useDynamicLogger) {
		setUseDynamicLogger(useDynamicLogger);
	}

	@Override
	public Object monitorPerformance(String declaringClass, String method, boolean userBound, MethodProceeding methodProceeding) throws Throwable {
		Logger logger = getLoggerForInvocation(declaringClass);
		if (isInterceptorEnabled(logger)) {
			return invokeUnderTrace(declaringClass, method, userBound, methodProceeding, logger);
		} else {
			return methodProceeding.proceed();
		}
	}


	protected String createInvocationTraceName(String declaringClass, String method) {
		StringBuilder sb = new StringBuilder(prefix);
		sb.append(declaringClass);
		sb.append('.').append(method);
		sb.append(suffix);
		return sb.toString();
	}

	protected Logger getLoggerForInvocation(String declaringClass) {
		if (this.defaultLogger != null) {
			return this.defaultLogger;
		} else {
			return LoggerFactory.getLogger(declaringClass);
		}
	}

	protected Object invokeUnderTrace(String declaringClass, String method, boolean userBound, MethodProceeding methodProceeding, Logger logger)
			throws Throwable {
		String name = createInvocationTraceName(declaringClass, method);
		StopWatch stopWatch = new StopWatch(name);
		stopWatch.start(name);
		try {
			return methodProceeding.proceed();
		} finally {
			stopWatch.stop();
			if (userBound && securityContextHolderStrategy != null) {
				Object principal = securityContextHolderStrategy.getContext().getAuthentication().getName();
				logger.trace(LogStatement.PRINCIPAL_MARKER_START + principal + LogStatement.PRINCIPAL_MARKER_END + stopWatch.shortSummary());
			} else {
				logger.trace(stopWatch.shortSummary());
			}
		}
	}

	protected boolean isInterceptorEnabled(Logger logger) {
		return isLogEnabled(logger);
	}

	protected boolean isLogEnabled(Logger logger) {
		return logger.isTraceEnabled();
	}

	public void setUseDynamicLogger(boolean useDynamicLogger) {
		// Release default logger if it is not being used.
		this.defaultLogger = (useDynamicLogger ? null : LoggerFactory.getLogger(getClass()));
	}

	public void setPrefix(String prefix) {
		this.prefix = (prefix != null ? prefix : "");
	}

	public void setSuffix(String suffix) {
		this.suffix = (suffix != null ? suffix : "");
	}

	public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
		this.securityContextHolderStrategy = securityContextHolderStrategy;
	}

}
